//useState
import {
    useState,
    useRef,
    createContext,
    useContext,
    useEffect,
    useReducer,
    useMemo,
    memo,
    useCallback,
    forwardRef, useImperativeHandle
} from 'react'
// css类名的使用
import './index.css'
import Counter from "./Counter";
import Status from "./Status";

function Button() {
    return <button>点我</button>
}
const num = 100
function getName() {
    return '小明'
}
const list = [
    {id: 1001, name: 'vue'},
    {id: 1002, name: 'react'},
    {id: 1003, name: 'angular'}
]
const isLogin = true
const articleType = 1 //0 1 3
function getArticleTem() {
    if (articleType === 1) {
        return <div>我是无图文章</div>
    } else if (articleType === 0) {
        return <div>我是单图模式</div>
    } else {
        return <div>我是三图模式</div>
    }
}
const styleObj = {
    color: 'blue',
    fontSize: '36px',
    fontWeight: 'bold'
}
//   顶层组件暴漏值，底层组件使用值
const MsgContext = createContext()
const url = 'https://api.oioweb.cn/api/common/HotList'
//   使用useMemo
function fib(n) {
    if (n < 3) {
        return 1
    }
    console.log('求数列被调用的')
    return fib(n - 1) + fib(n - 1)
}

// 项目的根组件
function App(defaultValue) {
    // 方法的使用
    const handleClick = (e) => {
        console.log(e)
    }
    const handleClick1 = (name) => {
        console.log(name)
    }
    const handleClick2 = (name, e) => {
        console.log(name, e)
    }
    //useState
    const [count, setCount] = useState(0)
    const [obj, setObj] = useState({name: '小明', age: 18})
    const plus = () => {
        setCount(count + 1)
    }
    const updateObj = () => {
        setObj({
            name: '小刚',
            age: 22
        })
    }
    //   双向数据绑定
    const [value, setValue] = useState('')
    const valueChange = (value) => {
        setValue(value)
    }
    //   获取dom元素
    //   渲染完毕之后dom生成之后才可用
    const inputRef = useRef(null)
    const getDom = () => {
        console.log(inputRef)
        console.dir(inputRef.current)
    }
    //   组件之间传值
    //props可以传递 数字、字符串、布尔值、数组、对象、函数、JSX。props是只读的
    const propName = '我是父组件里的变量'

    //子组件通过父组件给子组件传的方法给父组件传值
    // 把子组件传过来的值在父组件里进行视图更新
    const [msg, setMsg] = useState('')
    const getMsg = (msg) => {
        setMsg(msg)
        console.log(msg)
    }
    // 兄弟组件传值
    const [brotherName, setBrotherName] = useState('')
    const getBrotherName = (name) => {
        console.log(name)
        setBrotherName(name)
    }
    //顶层组件暴漏值，底层组件使用值
    const ContextValue = '我是顶层组件里的值'
    //useEffect
    const [HotList, setHotList] = useState([])
    useEffect(() => {
        async function getHotList() {
            const res = await fetch(url)
            const list = await res.json()
            console.log(Object.keys(list.result))
            let tempList = Object.keys(list.result)
            setHotList(tempList)
        }

        getHotList()
    }, [])
    //   使用useEffect的清除定时器的功能
    const [show, setShow] = useState(true)
    // reducer
// 1.定义reducer函数 根据不同的action 返回不同的状态
    function reducer(state, action) {
        switch (action.type) {
            case 'INC':
                return state + 1
            case 'DEC':
                return state - 1
            case 'SET':
                return action.payload
            default:
                return state
        }
    }

// 2.组件中调用useReducer(reducer,0)=>[state,dispatch]
// 3.调用dispatch({type:'INC})=>通知reducer产生一个新的状态 使用这个新状态更新UI
    //   使用reducer
    const [state, dispatch] = useReducer(reducer, 0)
    //   使用useMemo
    let [count1, setCount1] = useState(5)
    // const result = fib(count1)
    console.log('组件重新渲染了')
    const result1 = useMemo(() => {
        return fib(count1)
    }, [count1])
    let [count2, setCount2] = useState(0)

    let [count3, setCount3] = useState(0)
    // 使用组件memo基本数据类型不会导致子组件重新渲染，但是引用类型会导致重新渲染，可以通过useMemo解决引用类型导致子组件重新渲染
    let [count4, setCount4] = useState([])
    let list = useMemo(()=>{
        return [1,2,3]
    },[])
    // 使用useCallBack解决传递方法导致子组件重新渲染的问题
    // 传给子组件的函数
    const changeHandler = useCallback((value) => {
        console.log(value)},[])
    const [count5,setCount5] = useState(0)
    // 组件引用的透传：在父组件里拿到子组件里的组件的引用
    const sonRef = useRef(null)
    // 暴漏方法
    const sonRef1 = useRef(null)
    const showRef = () => {
        console.log(sonRef)
    }
    // 暴漏方法
    const showRef1 = () => {
        console.log(sonRef1)
        sonRef1.current.focusHandler()
    }
    return (
        <div className="App" style={{display: 'flex'}}>
            <div className="l" style={{flex: 1}}>
                <h2>表达式的展示</h2>
                <div>
                    {'1,使用引号传递字符串'} <br/>
                    2,使用js变量{num} <br/>
                    3,函数调用和方法调用{getName()} 和
                    {new Date().getDate()}<br/>
                    4,使用js对象 <div style={{color: 'red'}}>我是红色</div> <br/>
                    <h3>使用map方法遍历渲染列表,一定要写key值</h3>
                    <ul>
                        {list.map(item => <li key={item.id}>{item.name}</li>)}
                    </ul>
                    <h3>基础条件渲染</h3>
                    逻辑与
                    {isLogin && <span>我是span标签</span>}
                    三元运算 <br/>
                    {isLogin ? <span>三元运算真</span> : <span>三元运算假</span>}
                    <h3>复杂条件渲染</h3>
                    {/*通过传参来决定渲染什么模块*/}
                    {getArticleTem()}
                </div>
                {/*事件绑定*/}
                <h2>事件绑定</h2>
                <div>
                    <button onClick={handleClick}>事件绑定</button>
                    <button onClick={() => handleClick1('小明')}>传参事件绑定</button>
                    <button onClick={(e) => handleClick2('小明', e)}>传参+$event对象事件绑定</button>
                </div>
                <h2>使用组件</h2>
                <Button></Button>
                <Button/>
                <h2>useState</h2>
                <button onClick={plus}>使用useState更新基本数据—{count}</button>
                <button onClick={updateObj}>使用useState更新对象—{obj.name}今年{obj.age}</button>
                <h2>样式的使用</h2>
                <div>
                    <div style={{color: 'red', fontSize: '30px', fontWeight: 'bold'}}>我是红色</div>
                    <div style={styleObj}>我的样式</div>
                    <div className="styleTest">我的样式</div>
                </div>
                <h2>react的双向数据绑定</h2>
                <h4>{value}</h4>
                <input type="text" value={value} onChange={(e) => valueChange(e.target.value)}/>
                <h2>获取DOM</h2>
                <input type="text" ref={inputRef}/>
                <button onClick={getDom}>获取input的dom</button>
                <h2>组件之间传值</h2>
                <h3>父给子传参数</h3>
                <Son name={propName}
                     age={18} isTrue={false} list={['vue', 'react']} obj={{name: '小明'}}
                     cb={() => console.log('123')} child={<span>-我是span标签，我是JSX</span>}
                >
                    <span>子标签里的span</span>
                </Son>
                <h3>子给父传值，通过父传过来的方法</h3>
                <p>{msg}</p>
                <Son1 onGetMsg={getMsg}></Son1>
                <h2>兄弟组件传值</h2>
                <Brother1 onGetBrotherName={getBrotherName}></Brother1>
                <Brother2 brotherName={brotherName}></Brother2>
                <h2>顶层组件暴漏值，底层组件使用值</h2>
                <div>
                    <MsgContext.Provider value={ContextValue}>
                        <SecondEle></SecondEle>
                    </MsgContext.Provider>
                </div>
                <h2>useEffect的使用</h2>
                <ul style={{height: '100px', overflowY: 'auto'}}>
                    {HotList.map(item => <li>{item}</li>)}
                </ul>
                <h2>在useEffect清除定时器</h2>
                {show && <ForUseEffect/>}
                <button onClick={() => setShow(false)}>清除定时器</button>
            </div>
            <div className="r" style={{background: 'red', flex: 1}}>
                <h2>使用hooks</h2>
                <ForHooks></ForHooks>
                <h2>使用reducer</h2>
                <p>state:{state}</p>
                <button onClick={() => dispatch({type: 'DEC'})}>-</button>
                <button onClick={() => dispatch({type: 'INC'})}>+</button>
                <button onClick={() => dispatch({type: 'SET', payload: 100})}>update</button>
                <h2>使用useMemo</h2>
                <p>count1:{count1}</p>
                {/*<p>result:{result}</p>*/}
                <p>result1:{result1}</p>
                <button onClick={() => setCount1(count1++)}>更新count1</button>
                <button onClick={() => setCount2(count2++)}>更新count2</button>
                <h2>使用Memo的子组件</h2>
                <button onClick={() => setCount3(count3++)}>++</button>
                <button onClick={() => setCount4(count4++)}>setCount4</button>
                {/*<MemoSonRes count={count4}></MemoSonRes>*/}
                <MemoSonRes list={list}></MemoSonRes>
                <Input onChange={changeHandler}></Input>
                <button onClick={()=>setCount5(count5+1)}>{count5}</button>
                <h2>组件引用的透传：在父组件里拿到子组件里的组件的引用，并通过react的api把子组件里的方法暴露给父组件并在父组件里调用</h2>
                <Son2 ref={sonRef}></Son2>
                <button onClick={showRef}>拿组件引用</button>
                <Son3 ref={sonRef1}></Son3>
                <button onClick={showRef1}>拿组件引用</button>
                <h2>类写法的组件</h2>
                <Counter></Counter>
                <h2>zustand的使用</h2>
                <Status></Status>
            </div>
        </div>
    );
}
export default App;

function Son(propName, {onGetMsg}) {
    // 接收父组件穿的属性
    console.log('我是父组件传递的属性-  ', propName)
    // 子组件给父组件传参
    const msg = '我是子组件里的msg'
    console.log(onGetMsg)
    return (
        <div className="Son" onClick={() => onGetMsg(msg)}>
            我是儿子组件-- {propName.name}
            {propName.child}
            {/*子组件标签里传递过来的标签*/} <br/>
            {propName.children}
            on
        </div>
    )
}
// 子给父传值
function Son1({onGetMsg}) {
    // 子组件给父组件传参
    const msg = '-我是子组件里的msg'
    console.log(onGetMsg)
    return (
        <div className="Son" onClick={() => onGetMsg(msg)}>
            我是子组件1
        </div>
    )
}
function Brother1({onGetBrotherName}) {
    return (
        <div>
            <button onClick={() => onGetBrotherName('我是哥哥')}>我是哥哥</button>
        </div>
    )
}
function Brother2({brotherName}) {
    return (
        <div>
            <p>我是弟弟-{brotherName}</p>
        </div>
    )
}
function SecondEle() {
    return (
        <div>
            我是第二层组件
            <ThirdEle></ThirdEle>
        </div>
    )
}
function ThirdEle() {
    const msg = useContext(MsgContext)
    return (
        <div>
            我是第三层组件-{msg}
        </div>
    )
}
//清除useEffect的副作用
function ForUseEffect() {
    useEffect(() => {
        let timerId = setInterval(() => {
            console.log('定时器在执行')
        }, 1000)
        return () => {
            clearInterval(timerId)
        }
    }, [])
    return <div>我是UseEffect功能的组件</div>
}
// 自定义的hooks hooks：把独立的功能封装到一个函数里头
function useToggle() {
    const [hookValue, setHookValue] = useState(true)
    const toggle = () => {
        setHookValue(!hookValue)
    }
    //给外界使用的值，return出去
    return {hookValue, toggle}
}
// 使用hooks
function ForHooks() {
    //   使用hoods
    const {hookValue, toggle} = useToggle()
    return (
        <div>
            {hookValue && <div>我是div</div>}
            <button onClick={toggle}>toggle</button>
        </div>
    )
}
// 1验证默认的渲染机制
// 使用memo进行缓存
// const MemoSonRes = memo(function MemoSon({count}) {
//     console.log('MemoSon重新渲染了，我是子组件')
//     return <div>使用子组件缓存功能，props不改变子组件不改变,{count}</div>
// })
const MemoSonRes = memo(function MemoSon({list}) {
    console.log('MemoSon重新渲染了，我是子组件-----------')
    return <div>使用子组件缓存功能，props不改变子组件不改变,{list[0]}</div>
})
// 缓存组件与函数的传递
const Input = memo(function Input({onChange}) {
    console.log('我是input我被重新渲染了')
    return <input type="text" onChange={(e)=>onChange(e.target.value)}/>
})
// 组件引用的透传：在父组件里拿到子组件里的组件的引用
const Son2 = forwardRef((props,ref)=>{
    return <input type="text" ref={ref}/>
})
// 调方法
const Son3 = forwardRef((props,ref)=>{
    const inputRef = useRef(null)
    const focusHandler = () => {
        inputRef.current.focus()
    }
    // 把方法暴漏给父组件
    useImperativeHandle(ref,()=>{
        return {
            focusHandler
        }
    })
    return <input type="text" ref={inputRef}/>
})

